//==========================================================================================
//
//		OpenNETCF.Runtime.InteropServices.InteropMarshal
//		Copyright (c) 2003, OpenNETCF.org
//
//		This library is free software; you can redistribute it and/or modify it under 
//		the terms of the OpenNETCF.org Shared Source License.
//
//		This library is distributed in the hope that it will be useful, but 
//		WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
//		FITNESS FOR A PARTICULAR PURPOSE. See the OpenNETCF.org Shared Source License 
//		for more details.
//
//		You should have received a copy of the OpenNETCF.org Shared Source License 
//		along with this library; if not, email licensing@opennetcf.org to request a copy.
//
//		If you wish to contact the OpenNETCF Advisory Board to discuss licensing, please 
//		email licensing@opennetcf.org.
//
//		For general enquiries, email enquiries@opennetcf.org or visit our website at:
//		http://www.opennetcf.org
//
//==========================================================================================
using System;
using System.Text;
using System.Runtime.InteropServices;

namespace OpenNETCF.Runtime.InteropServices 
{
	/// <summary>
	/// Advanced marshalling functions for P/Invoke.
	/// </summary>
	public class InteropMarshal 
	{		
		#region Read functions

		#region IntPtr
		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <returns></returns>
		public static IntPtr ReadIntPtr(IntPtr ptr, int ofs)
		{
			int i = Marshal.ReadInt32(ptr,ofs);
			return new IntPtr(i);
		}
		#endregion

		#region Int32
		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <returns></returns>
		public static Int32 ReadInt32(IntPtr ptr, int ofs)
		{
			byte[] data = new byte[4];
			Marshal.Copy(new IntPtr((int)ptr + ofs), data, 0, 4);

			return BitConverter.ToInt32(data,0);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <returns></returns>
		public static UInt32 ReadUInt32(IntPtr ptr, int ofs)
		{
			byte[] data = new byte[4];
			Marshal.Copy(new IntPtr((int)ptr + ofs), data, 0, 4);

			return BitConverter.ToUInt32(data,0);
		}
		#endregion

		#region Int16
		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <returns></returns>
		public static Int16 ReadInt16(IntPtr ptr, int ofs)
		{
			byte[] data = new byte[2];
			Marshal.Copy(new IntPtr((int)ptr + ofs), data, 0, 2);

			return BitConverter.ToInt16(data,0);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <returns></returns>
		public static UInt16 ReadUInt16(IntPtr ptr, int ofs)
		{
			byte[] data = new byte[2];
			Marshal.Copy(new IntPtr((int)ptr + ofs), data, 0, 2);

			return BitConverter.ToUInt16(data,0);
		}
		#endregion

		#region String
		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <param name="len"></param>
		/// <returns></returns>
		public static string PtrToStringUni(IntPtr ptr, int ofs, int len)
		{
			int cb = len * Marshal.SystemDefaultCharSize;
			byte[] data = new byte[cb];
			Marshal.Copy(new IntPtr((int)ptr + ofs), data, 0, cb);

			string s = Encoding.Unicode.GetString(data, 0, len);
			
			int nullpos = s.IndexOf('\0');
			if(nullpos > -1)
				s = s.Substring(0,nullpos);

			return s;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <param name="len"></param>
		/// <returns></returns>
		public static string PtrToStringAnsi(IntPtr ptr, int ofs, int len)
		{
			int cb = len;
			byte[] data = new byte[cb];
			Marshal.Copy(new IntPtr((int)ptr + ofs), data, 0, cb);

			string s = Encoding.ASCII.GetString(data, 0, len);
			
			int nullpos = s.IndexOf('\0');
			if(nullpos > -1)
				s = s.Substring(0,nullpos);

			return s;
		}
		#endregion

		#region Char
		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <returns></returns>
		public static char ReadChar(IntPtr ptr, int ofs)
		{
			byte[] data = new byte[Marshal.SystemDefaultCharSize];
			Marshal.Copy(new IntPtr((int)ptr + ofs),data, 0, data.Length);

			return BitConverter.ToChar(data,0);
		}
		#endregion

		#region Byte[]
		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <param name="len"></param>
		/// <returns></returns>
		public static byte[] ReadByteArray(IntPtr ptr, int ofs, int len)
		{
			byte[] data = new byte[len];
			Marshal.Copy(new IntPtr((int)ptr + ofs), data, 0, len);

			return data;
		}
		#endregion

		#region Int64
		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <returns></returns>
		public static Int64 ReadInt64(IntPtr ptr, int ofs)
		{
			byte[] data = new byte[8];
			Marshal.Copy(new IntPtr((int)ptr + ofs), data, 0, 8);

			return BitConverter.ToInt64(data,0);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <returns></returns>
		public static UInt64 ReadUInt64(IntPtr ptr, int ofs)
		{
			byte[] data = new byte[8];
			Marshal.Copy(new IntPtr((int)ptr + ofs), data, 0, 8);

			return BitConverter.ToUInt64(data,0);
		}
		#endregion

		#region Bool

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <returns></returns>
		public static bool ReadBool(IntPtr ptr, int ofs)
		{
			bool b = false;

			byte[] data = new byte[4];
			Marshal.Copy(new IntPtr((int)ptr + ofs), data, 0, 4);

			b = Convert.ToBoolean(data);

			return b;
		}

		#endregion

		// TODO: byte

		#endregion

		#region Write functions

		#region Int32

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <param name="val"></param>
		public static void WriteIntPtr(IntPtr ptr, int ofs, IntPtr val)
		{
			InteropMarshal.WriteInt32(ptr, ofs, (int)val);
		}
		
		#endregion

		#region Int32

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <param name="val"></param>
		public static void WriteInt32(IntPtr ptr, int ofs, int val)
		{
			byte[] data = BitConverter.GetBytes(val);
			Marshal.Copy(data, 0, new IntPtr((int)ptr + ofs), data.Length);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <param name="val"></param>
		public static void WriteUInt32(IntPtr ptr, int ofs, uint val)
		{
			byte[] data = BitConverter.GetBytes(val);
			Marshal.Copy(data, 0, new IntPtr((int)ptr + ofs), data.Length);
		}

		#endregion

		#region Int16

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <param name="val"></param>
		public static void WriteInt16(IntPtr ptr, int ofs, Int16 val)
		{
			byte[] data = BitConverter.GetBytes(val);
			Marshal.Copy(data, 0, new IntPtr((int)ptr + ofs), data.Length);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <param name="val"></param>
		public static void WriteUInt16(IntPtr ptr, int ofs, UInt16 val)
		{
			byte[] data = BitConverter.GetBytes(val);
			Marshal.Copy(data, 0, new IntPtr((int)ptr + ofs), data.Length);
		}

		#endregion

		#region String

		#region ANSI String

		/// <summary>
		/// 
		/// </summary>
		/// <param name="s"></param>
		/// <returns></returns>
		public static IntPtr StringToHGlobalAnsi(string s)
		{
			if(s == null)
				return IntPtr.Zero;

			int i = s.Length * Marshal.SystemDefaultCharSize;
			IntPtr ptr = LocalAlloc(0, (uint)(i+1));

			byte[] data = Encoding.ASCII.GetBytes(s);
			Marshal.Copy(data, 0, ptr, data.Length);

			return ptr;
		}

		#endregion

		#region Unicode String

		/// <summary>
		/// 
		/// </summary>
		/// <param name="s"></param>
		/// <returns></returns>
		public static IntPtr StringToHGlobalUni(string s)
		{
			if(s == null)
				return IntPtr.Zero;
	
			int i = s.Length;
			
			IntPtr ptr = LocalAlloc(0,(uint)(2 * 1 + i));

			byte[] data = Encoding.Unicode.GetBytes(s);
			Marshal.Copy(data, 0, ptr, data.Length);

			return ptr;
		}

		#endregion

		#endregion

		#region Char

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <param name="val"></param>
		public static void WriteChar(IntPtr ptr, int ofs, char val)
		{
			byte[] data = BitConverter.GetBytes(val);
			Marshal.Copy(data, 0, new IntPtr((int)ptr + ofs), data.Length);
		}

		#endregion

		#region Byte[]

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <param name="val"></param>
		public static void WriteByteArray(IntPtr ptr, int ofs, byte[] val)
		{
			Marshal.Copy(val, 0, new IntPtr((int)ptr + ofs), val.Length);
		}

		#endregion

		#region Int64

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <param name="val"></param>
		public static void WriteInt64(IntPtr ptr, int ofs, Int64 val)
		{
			byte[] data = BitConverter.GetBytes(val);
			Marshal.Copy(data, 0, new IntPtr((int)ptr + ofs), data.Length);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <param name="val"></param>
		public static void WriteUInt64(IntPtr ptr, int ofs, UInt64 val)
		{
			byte[] data = BitConverter.GetBytes(val);
			Marshal.Copy(data, 0, new IntPtr((int)ptr + ofs), data.Length);
		}

		#endregion

		#region Bool

		/// <summary>
		/// 
		/// </summary>
		/// <param name="ptr"></param>
		/// <param name="ofs"></param>
		/// <param name="val"></param>
		public static void WriteBool(IntPtr ptr, int ofs, bool val)
		{
			byte[] data = BitConverter.GetBytes(val);
			Marshal.Copy(data, 0, new IntPtr((int)ptr + ofs), data.Length);
		}

		#endregion

		// TODO: byte

		#endregion

		#region Helper Functions
		private const uint LMEM_FIXED = 0x0000;
		private const uint LMEM_MOVEABLE = 2;
		private const uint LMEM_ZEROINIT  = 0x0040;
		private const uint LPTR = (LMEM_FIXED | LMEM_ZEROINIT);
		private static readonly IntPtr HIWORDMASK = new IntPtr((long)-65536); 

		private static bool IsNotWin32Atom(IntPtr ptr)
		{
			long b;
			b = (long)ptr;
			return (((long) 0) != (b & (long)InteropMarshal.HIWORDMASK)); 
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="cb"></param>
		/// <returns></returns>
		public static IntPtr AllocHGlobal(int cb)
		{
			IntPtr ptr = LocalAlloc(0, (uint)cb);
			if (ptr == IntPtr.Zero)
			{
				throw new OutOfMemoryException();
 
			}
			return ptr; 
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="cb"></param>
		/// <returns></returns>
		public static IntPtr AllocHGlobal(IntPtr cb)
		{
			return InteropMarshal.AllocHGlobal((int)cb); 			
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="hGlobal"></param>
		public static void FreeHGlobal(IntPtr hGlobal)
		{
			if(InteropMarshal.IsNotWin32Atom(hGlobal))
			{
				LocalFree(hGlobal);
			}
			else 
			{
				throw new ArgumentException("hGlobal is not an unmanaged atom.");
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="cb"></param>
		/// <returns></returns>
		public static IntPtr AllocHLocal(uint cb)
		{
			return LocalAlloc(LPTR, cb);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="hMem"></param>
		public static void FreeHLocal(IntPtr hMem)
		{
			if(hMem != IntPtr.Zero)
				LocalFree(hMem);
		}
		
		/// <summary>
		/// 
		/// </summary>
		/// <param name="time_t"></param>
		/// <returns></returns>
		public static DateTime Time_tToDateTime(uint time_t) 
		{
			long win32FileTime = 10000000*(long)time_t + 116444736000000000;
			return DateTime.FromFileTimeUtc(win32FileTime);
		}

		#endregion

		#region API Prototypes
#if DEVICE
		[DllImport("coredll.dll",EntryPoint="LocalAlloc",SetLastError=true)]
#else
		[DllImport("kernel32.dll",EntryPoint="LocalAlloc",SetLastError=true)]
#endif
		internal static extern IntPtr LocalAlloc(uint uFlags, uint Bytes);

#if DEVICE
		[DllImport("coredll.dll",EntryPoint="LocalFree",SetLastError=true)]
#else
		[DllImport("kernel32.dll",EntryPoint="LocalFree",SetLastError=true)]
#endif		
		internal static extern IntPtr LocalFree(IntPtr hMem);
		#endregion
	}
}
